/*****************************************************************************
 *   Copyright 2005 Tim A Wang
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 ****************************************************************************/
package com.jb.framework;

import com.jb.web.application.configuration.Config;
import com.jb.web.application.configuration.DBConfig;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import java.util.Hashtable;
import java.util.Map;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * DataSourceManager is responsible for managing database connections.
 *
 * @author Tim Wang
 * @version 1.0
 */
public class DataSourceManager {
    static DataSourceManager containerManager = new DataSourceManager();
    protected static Log log = LogFactory.getLog(DataSourceManager.class.getName());
    static Map conns = new Hashtable();
    static ThreadLocal transaction;
    DBConfig dbConfig = Config.getInstance().getDBConfig();
    DataSource ds;

    public static DBConfig getConfig() {
        return containerManager.dbConfig;
    }
    
    public static Connection getConnection(Object callerId)
        throws SQLException {
        Connection conn = containerManager._getConnection(callerId);

        if (conn == null) {
            throw new SQLException("Failed to obtain connection");
        }

        return conn;
    }

    /**
     * Returns the singleton manager.
     *
     * @return Data source manager.
     */
    public static DataSourceManager getContainerManager() {
        return containerManager;
    }

    public static void closeTransaction() {
        Connection conn = (Connection) conns.get(transaction);

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                if(log.isErrorEnabled())
                    log.error(containerManager, e);
                //
            }
        }

        transaction = null;
    }

    public static void openTransaction() {
        transaction = new ThreadLocal();
    }

    public static void releaseConnection(Object callerId, Connection conn) {
        containerManager._releaseConnection(conn);
    }

    private Connection getFromCache(Object CallerId) {
        if (transaction != null) {
            return (Connection) conns.get(transaction);
        } else {
            return null;
        }
    }

    /**
     * Returns a connection.
     *
     * @param callerId id of the client who obtains the connection.
     *
     * @return JDBC connection
     *
     * @throws SQLException if fails to obtain connection.
     */
    private Connection _getConnection(Object callerId)
        throws SQLException {
        if (log.isDebugEnabled()) {
            log.debug("get connection");
        }
        Connection connection = getFromCache(callerId);
        if (connection == null) {
            if (dbConfig.isUseDataSource()) {
                connection = _getConnectionDS(callerId);
            } else {
                connection = _getConnectionDriver(callerId);
            }
            cacheConnection(callerId, connection);
        }
        return connection;
    }
//oracle database
//oracle.jdbc.driver.OracleDriver
//jdbc:oracle:thin:@localhost:1521:XE
//hr/welcome
    
    //mysql database
    //com.mysql.jdbc.Driver
    //jdbc:mysql://localhost/pcosc?autoReconnect=true
    //root/welcome
    
    
    //derby
    //jdbc:derby:derbyDB;create=true
    //user1
    //user1
    private Connection _getConnectionDS(Object callerId)
        throws SQLException {
        if (ds == null) {
            try {
                InitialContext context = new InitialContext();
                ds = (DataSource) context.lookup("java:comp/env/jdbc/" + dbConfig.getDataSourceName());
            } catch (NamingException e) {
                if(log.isErrorEnabled())
                    log.error(containerManager, e);
                return null;
            }
        }

        Connection connection = ds.getConnection();

        if (connection != null) {
            connection.setAutoCommit(false);
            dbConfig.decorate(connection);
        }

        return connection;
    }

    private Connection _getConnectionDriver(Object callerId)
        throws SQLException {
        try {
            Connection connection = null;
            Class.forName(dbConfig.getDriverName());
            connection = DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUserName(), dbConfig.getPassword());

            if (connection != null) {
                connection.setAutoCommit(false);
                dbConfig.decorate(connection);
            }

            return connection;
        } catch (Exception e) {
          
            if (log.isErrorEnabled()) {
                log.error(this, e);
            }

            throw new SQLException("Failed to get connection due to " + e.getMessage());
        }
    }

    /**
     * Releases the connection .
     *
     * @param conn parameter
     */
    private void _releaseConnection(Connection conn) {
        if (transaction != null) {
            return;
        }

        if (conn == null) {
            return;
        }

        if (log.isDebugEnabled()) {
            log.debug("release connection");
        }

        try {
            conn.close();
        } catch (SQLException e) {
            if (log.isErrorEnabled()) {
                log.error(this, e);
            }
        }
    }

    private void cacheConnection(Object callerId, Connection connection) {
        if (transaction != null) {
            this.conns.put(transaction, connection);
        }
    }
}
